// Copyright Leonid Shlyapnikov 2011
// Distributed under the GNU Lesser General Public License http://www.gnu.org/licenses/lgpl.html
//    author: Leonid Shlyapnikov, created on: Apr 2, 2011

#include <boost/test/test_tools.hpp>
#include <sstream>
#include <cstring>

#include <vector>

#include "../../main/fixparser/MemoryArea.h"
#include "../../main/fixparser/Field.h"
#include "../../main/fixparser/FieldList.h"
#include "../../main/fixparser/log.h"
#include "../../main/fixparser/util.h"


using fixparser::MemoryArea;
using fixparser::FieldList;
using fixparser::FieldListIterator;
using fixparser::Field;


Field* add_field_(MemoryArea* ma, FieldList* list, const char* tag, const char* value) {
    Field* f0 = ma->create<Field>();
    f0->setTag(tag);
    f0->setValue(value);
    list->push_back(f0);

    return f0;
}


void Vector_iterator() {
    using std::vector;

    vector<int> v;
    v.push_back(10);
    v.push_back(11);

    vector<int>::const_iterator test = v.begin();
    BOOST_CHECK_EQUAL(*test, 10);
    ++test;
    BOOST_CHECK_EQUAL(*test, 11);
    ++test;
    BOOST_CHECK(test == v.end());
}


void FieldList_push_back() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);

    // WHEN
    {
        Field* f0 = ma.create<Field>();
        f0->setTag("8");
        f0->setValue("FIX.4.2");
        list.push_back(f0);
    }
    {
        Field* f1 = ma.create<Field> ();
        f1->setTag("9");
        f1->setValue("99");
        list.push_back(f1);
    }

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 2);
    // 1st element
    FieldList::const_iterator it = list.begin();
    {
        BOOST_CHECK_EQUAL(it->tag().c_str(),    "8");
        BOOST_CHECK_EQUAL(it->value().c_str(),  "FIX.4.2");
        const Field& f = *it;
        BOOST_CHECK_EQUAL(f.tag().c_str(),      "8");
        BOOST_CHECK_EQUAL(f.value().c_str(),    "FIX.4.2");
        BOOST_CHECK(&f == &list[0]);
    }
    // 2nd element
    ++it;
    {
        BOOST_CHECK_EQUAL(it->tag().c_str(),    "9");
        BOOST_CHECK_EQUAL(it->value().c_str(),  "99");
        const Field& f = *it;
        BOOST_CHECK_EQUAL(f.tag().c_str(),      "9");
        BOOST_CHECK_EQUAL(f.value().c_str(),    "99");
        BOOST_CHECK(&f == &list[1]);
    }
    // end of list
    ++it;
    BOOST_CHECK(it == list.end());
    BOOST_CHECK(it.current() == 0);
    // no way, end of list, should throw up
    BOOST_CHECK_THROW(++it,     std::out_of_range);
    BOOST_CHECK_THROW(list[2],  std::out_of_range);
}


void FieldList_erase_first() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);
    add_field_(&ma, &list, "tag0", "val0");
    add_field_(&ma, &list, "tag1", "val1");
    add_field_(&ma, &list, "tag2", "val2");
    add_field_(&ma, &list, "tag3", "val3");
    add_field_(&ma, &list, "tag4", "val4");
    add_field_(&ma, &list, "tag5", "val5");
    BOOST_CHECK_EQUAL(list.size(), 6);

    // WHEN
    const Field* removed = list.erase(0);

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 5);
    BOOST_CHECK_EQUAL(removed->tag().c_str(),   "tag0");
    BOOST_CHECK_EQUAL(removed->value().c_str(), "val0");
    FieldList::const_iterator it = list.begin();
    BOOST_CHECK_EQUAL(it->tag().c_str(),      "tag1");
    BOOST_CHECK_EQUAL(it->value().c_str(),    "val1");
}


void FieldList_erase_last() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);
    add_field_(&ma, &list, "tag0", "val0");
    add_field_(&ma, &list, "tag1", "val1");
    add_field_(&ma, &list, "tag2", "val2");
    add_field_(&ma, &list, "tag3", "val3");
    add_field_(&ma, &list, "tag4", "val4");
    add_field_(&ma, &list, "tag5", "val5");
    BOOST_CHECK_EQUAL(list.size(), 6);

    // WHEN
    const Field* removed = list.erase(5); // remove last element

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 5);
    BOOST_CHECK_EQUAL(removed->tag().c_str(),   "tag5");
    BOOST_CHECK_EQUAL(removed->value().c_str(), "val5");
    FieldList::const_iterator it = list.begin();
    BOOST_CHECK_EQUAL(it->tag().c_str(),      "tag0");
    BOOST_CHECK_EQUAL(it->value().c_str(),    "val0");
    ++it;
    ++it;
    ++it;
    ++it;
    BOOST_CHECK_EQUAL(it->tag().c_str(),      "tag4");
    BOOST_CHECK_EQUAL(it->value().c_str(),    "val4");
    // end of list
    ++it;
    BOOST_CHECK(it == list.end());
    BOOST_CHECK(it.current() == 0);
    // end of list reached
    BOOST_CHECK_THROW(++it, std::out_of_range);
}


void FieldList_erase_in_the_middle() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);
    add_field_(&ma, &list, "tag0", "val0");
    add_field_(&ma, &list, "tag1", "val1");
    add_field_(&ma, &list, "tag2", "val2");
    add_field_(&ma, &list, "tag3", "val3");
    add_field_(&ma, &list, "tag4", "val4");
    add_field_(&ma, &list, "tag5", "val5");
    BOOST_CHECK_EQUAL(list.size(), 6);

    {
        // WHEN
        const Field* removed = list.erase(1);
        // THEN
        BOOST_CHECK_EQUAL(removed->tag().c_str(),   "tag1");
        BOOST_CHECK_EQUAL(removed->value().c_str(), "val1");
    }
    {
        // WHEN
        const Field* removed = list.erase(1);
        // THEN
        BOOST_CHECK_EQUAL(removed->tag().c_str(),   "tag2");
        BOOST_CHECK_EQUAL(removed->value().c_str(), "val2");
    }
    {
        // WHEN
        const Field* removed = list.erase(1);
        // THEN
        BOOST_CHECK_EQUAL(removed->tag().c_str(),   "tag3");
        BOOST_CHECK_EQUAL(removed->value().c_str(), "val3");
    }

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 3);
    {
        const Field& f = list[0];
        BOOST_CHECK_EQUAL(f.tag().c_str(),   "tag0");
        BOOST_CHECK_EQUAL(f.value().c_str(), "val0");
    }
    {
        const Field& f = list[1];
        BOOST_CHECK_EQUAL(f.tag().c_str(),   "tag4");
        BOOST_CHECK_EQUAL(f.value().c_str(), "val4");
    }
    {
        const Field& f = list[2];
        BOOST_CHECK_EQUAL(f.tag().c_str(),   "tag5");
        BOOST_CHECK_EQUAL(f.value().c_str(), "val5");
    }
}


void FieldList_out_of_range_erase() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);
    add_field_(&ma, &list, "tag0", "val0");
    add_field_(&ma, &list, "tag1", "val1");
    add_field_(&ma, &list, "tag2", "val2");
    add_field_(&ma, &list, "tag3", "val3");
    add_field_(&ma, &list, "tag4", "val4");
    add_field_(&ma, &list, "tag5", "val5");
    BOOST_CHECK_EQUAL(list.size(), 6);

    // out of range erase
    BOOST_CHECK_THROW(list.erase(-1), std::out_of_range);
    BOOST_CHECK_EQUAL(list.size(), 6);
    BOOST_CHECK_THROW(list.erase(6), std::out_of_range);
    BOOST_CHECK_EQUAL(list.size(), 6);
    BOOST_CHECK_THROW(list.erase(100), std::out_of_range);
    BOOST_CHECK_EQUAL(list.size(), 6);

    list.clear();
    BOOST_CHECK_EQUAL(list.size(), 0);
    BOOST_CHECK_THROW(list.erase(0), std::out_of_range);
    BOOST_CHECK_EQUAL(list.size(), 0);
}


void FieldList_empty() {
    // GIVEN
    MemoryArea ma(1024);
    FieldList list(&ma);
    const Field* head = debug_head_(list);
    BOOST_CHECK_EQUAL(list.size(), 0);
    BOOST_CHECK(list.front() == 0);
    BOOST_CHECK(head == list.back());
    BOOST_CHECK(head->next() == 0);

    // WHEN
    add_field_(&ma, &list, "tag0", "val0");

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 1);
    BOOST_CHECK_EQUAL(list.back(), &list[0]);
    BOOST_CHECK_EQUAL(head, debug_head_(list));
    BOOST_CHECK_NE(head, list.back());
    BOOST_CHECK(head->next() != 0);
    BOOST_CHECK(list.back()->next() == 0);

    // WHEN
    add_field_(&ma, &list, "tag1", "val1");

    // THEN
    BOOST_CHECK_EQUAL(list.size(), 2);
    BOOST_CHECK_EQUAL(list.back(), &list[1]);
    BOOST_CHECK_EQUAL(head, debug_head_(list));
    BOOST_CHECK_NE(head, list.back());
    BOOST_CHECK(head->next() != 0);
    BOOST_CHECK(list.back()->next() == 0);
}
